﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Lucene.Net.Documents;
using Microsoft.TeamFoundation.VersionControl.Client;
using NLog;

namespace TfsGiggle.Core
{
    /// <summary>
    /// This is what is indexed
    /// </summary>
    internal class SourceControlDocument
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        private static ThreadLocal<Field> _fieldDocumentKey = new ThreadLocal<Field>(() => new Field(FieldNames.DocumentKey, "", Field.Store.YES, Field.Index.NOT_ANALYZED));
        private static ThreadLocal<Field> _fieldModified = new ThreadLocal<Field>(() => new Field(FieldNames.Modified, "", Field.Store.NO, Field.Index.NOT_ANALYZED));
        private static ThreadLocal<Field> _fieldContents = new ThreadLocal<Field>(() => new Field(FieldNames.Contents, new StreamReader(Path.GetTempFileName())));
        private static ThreadLocal<Field> _fieldUsingDeclarations = new ThreadLocal<Field>(() => new Field(FieldNames.Using, "", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldClassDeclarations = new ThreadLocal<Field>(() => new Field(FieldNames.Class, "", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldInterfaceDeclarations = new ThreadLocal<Field>(() => new Field(FieldNames.Interface, "", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldMethodDeclarations = new ThreadLocal<Field>(() => new Field(FieldNames.Method, "", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldChangesetId = new ThreadLocal<Field>(() => new Field(FieldNames.ChangesetId, "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldChangesetCommitter = new ThreadLocal<Field>(() => new Field(FieldNames.ChangesetCommitter, "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldChangesetComment = new ThreadLocal<Field>(() => new Field(FieldNames.ChangesetComment, "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldWorkitemId = new ThreadLocal<Field>(() => new Field(FieldNames.WorkitemId, "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));
        private static ThreadLocal<Field> _fieldWorkitemTitle = new ThreadLocal<Field>(() => new Field(FieldNames.WorkitemTitle, "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));

        private SourceControlDocument() { }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="item"></param>
        /// <param name="teamProjectCollectionId"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        internal static Document Document(IItem item, string teamProjectCollectionId, string path)
        {
            if (item == null) throw new ArgumentNullException("item");
            if (string.IsNullOrWhiteSpace(teamProjectCollectionId)) throw new ArgumentNullException("teamProjectCollectionId");
            if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path");

            _logger.Trace("item:{0} teamProjectCollectionId:{1} path':{2}", item.ChangesetId, teamProjectCollectionId, path);

            // make a new, empty document
            Document doc = new Document();

            // Add the documentkey as a combination og teamprojectcollectionid and path
            // Use a field that is indexed (i.e. searchable), but don't tokenize the field into words.
            _fieldDocumentKey.Value.SetValue(IndexModel.GetDocumentKey(teamProjectCollectionId, path));
            doc.Add(_fieldDocumentKey.Value);

            // Add the last modified date of the file a field named "modified".  Use 
            // a field that is indexed (i.e. searchable), but don't tokenize the field
            // into words.
            // http://msdn.microsoft.com/en-us/library/az4se3k1.aspx#UniversalSortable
            _fieldModified.Value.SetValue(item.CheckinDate.ToUniversalTime().ToString("u"));
            doc.Add(_fieldModified.Value);

            // Specify a Reader,so that the text of the file is tokenized and indexed, but not stored.
            // Note that FileReader expects the file to be in the system's default encoding.
            // If that's not the case searching for special characters will fail.
            _fieldContents.Value.SetValue(new StreamReader(item.DownloadFile(), Encoding.GetEncoding(item.Encoding), true));
            doc.Add(_fieldContents.Value);

            if (item.UsingDeclarations != null && item.UsingDeclarations.Count() > 0)
            {
                _fieldUsingDeclarations.Value.SetValue(string.Join(Delimiter, item.UsingDeclarations));
                doc.Add(_fieldUsingDeclarations.Value);
            }

            if (item.ClassDeclarations != null && item.ClassDeclarations.Count() > 0)
            {
                _fieldClassDeclarations.Value.SetValue(string.Join(Delimiter, item.ClassDeclarations));
                doc.Add(_fieldClassDeclarations.Value);
            }

            if (item.InterfaceDeclarations != null && item.InterfaceDeclarations.Count() > 0)
            {
                _fieldInterfaceDeclarations.Value.SetValue(string.Join(Delimiter, item.InterfaceDeclarations));
                doc.Add(_fieldInterfaceDeclarations.Value);
            }

            if (item.MethodDeclarations != null && item.MethodDeclarations.Count() > 0)
            {
                _fieldMethodDeclarations.Value.SetValue(string.Join(Delimiter, item.MethodDeclarations));
                doc.Add(_fieldMethodDeclarations.Value);
            }

            var changesetHistory = item.VersionControlServer.QueryHistory(path, VersionSpec.Latest, 0, RecursionType.None, "", null, VersionSpec.Latest, Int32.MaxValue, true, false);
            _logger.Trace("{0} distinct changesets found for {1}", changesetHistory.Count(), path);

            var changesetIds = (from p in changesetHistory select p.ChangesetId.ToString()).ToArray();
            var changesetComments = (from p in changesetHistory select p.Comment).ToArray();
            var changesetCommitters = (from p in changesetHistory select p.Committer).ToArray();

            if (changesetIds != null && changesetIds.Length > 0)
            {
                _fieldChangesetId.Value.SetValue(string.Join(Delimiter, changesetIds));
                doc.Add(_fieldChangesetId.Value);
            }

            if (changesetComments != null && changesetComments.Length > 0)
            {
                _fieldChangesetComment.Value.SetValue(string.Join(Delimiter, changesetComments));
                doc.Add(_fieldChangesetComment.Value);
            }

            if (changesetCommitters != null && changesetCommitters.Length > 0)
            {
                _fieldChangesetCommitter.Value.SetValue(string.Join(Delimiter, changesetCommitters));
                doc.Add(_fieldChangesetCommitter.Value);
            }

            var workItems = new Dictionary<int, string>();
            foreach (var singleChangeset in changesetHistory)
            {
                TfsWorkItem.AddWorkItems(singleChangeset, workItems);
            }
            _logger.Trace("{0} distinct workitens found for {1}", workItems.Count(), path);

            if (workItems != null && workItems.Count > 0)
            {
                _fieldWorkitemId.Value.SetValue(string.Join(Delimiter, (from p in workItems.Keys select p.ToString()).ToArray()));
                doc.Add(_fieldWorkitemId.Value);

                _fieldWorkitemTitle.Value.SetValue(string.Join(Delimiter, (from p in workItems.Values select p).ToArray()));
                doc.Add(_fieldWorkitemTitle.Value);
            }
            _logger.Trace("exit");
            return doc;
        }

        /// <summary>
        /// Field value delimiter
        /// </summary>
        public static string Delimiter
        {
            get
            { 
                return CharDelimiter.ToString(); 
            }
        }

        /// <summary>
        /// Field value delimiter
        /// </summary>
        public static char CharDelimiter
        {
            get
            {
                return '§';
            }
        }
    }
}